package hn.cch.advanced.concurrent.atomic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 解决ABA问题
 */
public class AtomicStampedReferenceDemo {

    private static final Logger logger = LoggerFactory.getLogger(AtomicStampedReferenceDemo.class);


    private AtomicReference<String> reference;
    private AtomicStampedReference<String> stamped; // 记录变动次数
    private AtomicMarkableReference<String> markable; // 不关注变动次数，只关注是否变动

    public static void main(String[] args) throws InterruptedException {

        testReference();
        testStamped();
        testMarkable();

    }

    public static void testReference() throws InterruptedException {
        AtomicStampedReferenceDemo demo = new AtomicStampedReferenceDemo();
        demo.reference = new AtomicReference<>("A");
        String get = demo.reference.get();
        logger.debug("get={};", get);

        Thread.sleep(300);
        new Thread(() -> {
            logger.debug("A->B:{}", demo.reference.compareAndSet(demo.reference.get(), "B"));
        }).start();
        Thread.sleep(500);
        new Thread(() -> {
            logger.debug("B->A:{}", demo.reference.compareAndSet(demo.reference.get(), "A"));
        }).start();

        Thread.sleep(900);
        new Thread(() -> {
            logger.debug("A->C:{}", demo.reference.compareAndSet(get, "C"));
        }).start();

    }

    public static void testStamped() throws InterruptedException {
        AtomicStampedReferenceDemo demo = new AtomicStampedReferenceDemo();
        demo.stamped = new AtomicStampedReference<>("A", 0);
        String reference = demo.stamped.getReference();
        int stamp = demo.stamped.getStamp();
        logger.debug("reference={},stamp={};", reference, stamp);
        Thread.sleep(300);
        new Thread(() -> {
            logger.debug("A->B:{}", demo.stamped.compareAndSet(
                    demo.stamped.getReference(), "B", demo.stamped.getStamp(), demo.stamped.getStamp() + 1));
        }).start();
        Thread.sleep(500);
        new Thread(() -> {
            logger.debug("B->A:{}", demo.stamped.compareAndSet(
                    demo.stamped.getReference(), "A", demo.stamped.getStamp(), demo.stamped.getStamp() + 1));
        }).start();

        Thread.sleep(900);
        new Thread(() -> {
            logger.debug("A->C:{}", demo.stamped.compareAndSet(reference, "C", stamp, stamp + 1));
        }).start();
    }

    public static void testMarkable() throws InterruptedException {
        AtomicStampedReferenceDemo demo = new AtomicStampedReferenceDemo();
        demo.markable = new AtomicMarkableReference<>("A", true);
        String reference = demo.markable.getReference();
        logger.debug("reference={};", reference);
        Thread.sleep(300);
        new Thread(() -> {
            logger.debug("A->B:{}", demo.markable.compareAndSet(
                    demo.markable.getReference(), "B", true, false));
        }).start();
        Thread.sleep(500);
        new Thread(() -> {
            logger.debug("B->A:{}", demo.markable.compareAndSet(
                    demo.markable.getReference(), "A", false, true));
        }).start();

        Thread.sleep(900);
        new Thread(() -> {
            logger.debug("A->C:{}", demo.markable.compareAndSet(reference, "C", true, false));
        }).start();
    }


}
